package cn.xinfei.xdecision.common.service.enginex.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.xinfei.xdecision.common.dao.enginex.EngineNodeMapper;
import cn.xinfei.xdecision.common.model.component.ComponentVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVersionVo;
import cn.xinfei.xdecision.common.model.component.decisiontable.vo.DecisionTablesVo;
import cn.xinfei.xdecision.common.model.component.decisiontree.vo.DecisionTreeVersionVo;
import cn.xinfei.xdecision.common.model.component.decisiontree.vo.DecisionTreeVo;
import cn.xinfei.xdecision.common.model.component.dependency.EngineNodeComponentRel;
import cn.xinfei.xdecision.common.model.component.dependency.resp.DecisionComponentDependencyResp;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleInfo;
import cn.xinfei.xdecision.common.model.component.guiderule.RuleVersion;
import cn.xinfei.xdecision.common.model.component.guiderule.param.RuleSetNodeResultParam;
import cn.xinfei.xdecision.common.model.component.scorecard.vo.ScorecardVersionVo;
import cn.xinfei.xdecision.common.model.component.scriptrule.RuleScriptVersion;
import cn.xinfei.xdecision.common.model.decisionflow.*;
import cn.xinfei.xdecision.common.model.enginex.po.EngineAction;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNodeFieldRel;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.model.enginex.vo.ActionListVo;
import cn.xinfei.xdecision.common.model.enginex.vo.EngineUpdateReqVo;
import cn.xinfei.xdecision.common.model.enginex.vo.NodeVo;
import cn.xinfei.xdecision.common.model.enginex.vo.response.param.*;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.component.ComponentCommonService;
import cn.xinfei.xdecision.common.service.component.decisiontable.DecisionTablesService;
import cn.xinfei.xdecision.common.service.component.decisiontree.DecisionTreeService;
import cn.xinfei.xdecision.common.service.component.dependency.AbstractDependency;
import cn.xinfei.xdecision.common.service.component.dependency.EngineNodeComponentRelService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleService;
import cn.xinfei.xdecision.common.service.component.guiderule.RuleVersionService;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardService;
import cn.xinfei.xdecision.common.service.component.scorecard.ScorecardVersionService;
import cn.xinfei.xdecision.common.service.component.scriptrule.RuleScriptVersionService;
import cn.xinfei.xdecision.common.service.enginex.EngineNodeFieldService;
import cn.xinfei.xdecision.common.service.enginex.EngineNodeService;
import cn.xinfei.xdecision.common.service.enginex.EngineService;
import cn.xinfei.xdecision.common.service.enginex.util.NodeUtil;
import cn.xinfei.xdecision.common.utils.constant.CommonConst;
import cn.xinfei.xdecision.common.utils.constant.Constants;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.action.ActionServerEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.action.OldModelActionEnum;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.python.google.common.collect.Sets;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum.CLASSIFY;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.*;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

@Service
@Slf4j
public class EngineNodeServiceImpl extends ServiceImpl<EngineNodeMapper, EngineNode> implements EngineNodeService {

    @Autowired
    public ScorecardService scorecardService;
    @Autowired
    private DecisionTablesService decisionTablesService;
    @Autowired
    private DecisionTreeService decisionTreeService;
    @Autowired
    private ScorecardVersionService scorecardVersionService;
    @Autowired
    private RuleVersionService ruleVersionService;
    @Autowired
    private RuleScriptVersionService ruleScriptVersionService;
    @Autowired
    private RuleService ruleService;
    @Resource
    private EngineVersionServiceImpl engineVersionService;

    @Resource
    private EngineNodeMapper engineNodeMapper;

    @Resource
    private EngineNodeFieldService engineNodeFieldService;

    @Resource
    private EngineNodeComponentRelService engineNodeComponentRelService;
    @Resource
    private NodeJsonParseService nodeJsonParseService;

    @Resource
    private EngineService engineService;

    @Resource
    private EngineActionServiceImpl engineActionService;


    private QueryWrapper<EngineNode> queryWrapper(String nodeCode, Long versionId) {
        QueryWrapper<EngineNode> queryWrapper = new QueryWrapper<>();
        //联合索引，version_id必须放在前面
        queryWrapper.eq("version_id", versionId);
        queryWrapper.eq("node_code", nodeCode);
        return queryWrapper;
    }

    private QueryWrapper<EngineNode> queryWrapperByVersionId(Long versionId) {
        QueryWrapper<EngineNode> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("version_id", versionId);
        return queryWrapper;
    }

    @Override
    public List<EngineNode> getEngineNodeListByEngineVersionId(Long engineVersionId) {
        QueryWrapper<EngineNode> queryWrapper = queryWrapperByVersionId(engineVersionId);
        queryWrapper.orderByAsc("node_order");
        return this.list(queryWrapper);
    }

    /**
     * 查询当前版本节点列表
     */
    @Override
    public NodeListRespVo getEngineNodeList(NodeListReqVo nodeListReqVo) {
        String engineCode = nodeListReqVo.getEngineCode();
        Long versionNo = nodeListReqVo.getVersionNo();

        EngineVersion engineVersion = engineVersionService.getByEngineCodeAndVersionNo(engineCode, versionNo);
        if (engineVersion == null) {
            throw new ApiException(ENGINE_VERSION_NOT_EXISTS.getCode(), ENGINE_VERSION_NOT_EXISTS.getMessage());
        }

        int maxNodeOrder = getMaxNodeOrder(engineVersion.getId());

        return NodeListRespVo.builder()
                .engineNodeList(engineVersion.getEngineNodeList())
                .maxOrder(maxNodeOrder)
                .versionId(engineVersion.getId())
                .engineCode(engineCode)
                .versionNo(versionNo)
                .build();

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public EngineNode updateNodeInfo(UpdateNodeInfoReqVo updateNodeInfoReqVo) {
        checkVersionStatus(updateNodeInfoReqVo.getVersionId());

        EngineNode engineNode = this.getOne(queryWrapper(updateNodeInfoReqVo.getNodeCode(), updateNodeInfoReqVo.getVersionId()));
        if (engineNode == null) {
            log.error("[更新节点属性]更新异常,节点不存在.nodeId:{},nodeCode:{}", updateNodeInfoReqVo.getNodeId(), updateNodeInfoReqVo.getNodeCode());
            throw exception(PARAM_CHECK_ERROR, "节点信息不存在");
        }

        EngineNode newNode = new EngineNode();
        BeanUtils.copyProperties(engineNode, newNode);
        newNode.setEngineCode(updateNodeInfoReqVo.getEngineCode());
        newNode.setNodeName(updateNodeInfoReqVo.getNodeName());
        newNode.setNodeJson(updateNodeInfoReqVo.getNodeJson());

        List<EngineNode> nodeList = getEngineNodeListByEngineVersionId(updateNodeInfoReqVo.getVersionId());
        newNode = branchNodeRemoveAllLink(newNode, nodeList);

        JSONObject parseResult = nodeJsonParseService.nodeJsonParser(newNode);
        String nodeScript = parseResult.getString("nodeScript");
        if (nodeScript != null) {
            newNode.setNodeScript(nodeScript);
        }

        Set<String> fieldCodes = Sets.newHashSet();
        //查询节点依赖的变量：组件中依赖的变量
        ComponentCommonService targetComponent = ComponentCommonService.getTargetComponentByNodeType(newNode.getNodeType());
        if (Objects.nonNull(targetComponent)) {
            List<String> varCodes = targetComponent
                    .getFieldCodesByComponentCodes(parseResult.getJSONArray("componentCodes").toJavaList(ComponentVo.class));
            if (CollectionUtils.isNotEmpty(varCodes)) {
                fieldCodes.addAll(varCodes);
            }
        }
        //查询节点依赖的变量：终止条件或分组、决策选项条件中依赖的变量
        JSONArray nodeJsonFieldCode = parseResult.getJSONArray("fieldCodes");
        if (!Objects.isNull(nodeJsonFieldCode)) {
            fieldCodes.addAll(nodeJsonFieldCode.toJavaList(String.class));
        }
        newNode.setFieldCodes(CollUtil.join(fieldCodes, ","));

        log.info("[更新节点属性]更新后节点信息:{},更新前节点信息:{}", newNode, engineNode);
        boolean isUpdated = this.update(newNode, queryWrapper(newNode.getNodeCode(), newNode.getVersionId()));

        //动作
        EngineAction engineAction = parseResult.getObject("engineAction", EngineAction.class);
        if (Objects.nonNull(engineAction)) {
            EngineAction oldEngineAction = engineActionService.getOne(new LambdaQueryWrapper<EngineAction>()
                    .eq(EngineAction::getVersionId, engineAction.getVersionId())
                    .eq(EngineAction::getNodeCode, engineAction.getNodeCode()));
            if (Objects.nonNull(oldEngineAction)) {
                engineAction.setId(oldEngineAction.getId());
            }
            engineActionService.saveOrUpdate(engineAction);
        }

        //保存节点变量依赖关系
        if (Objects.nonNull(nodeJsonFieldCode)) {
            saveNodeFieldRel(newNode, Sets.newHashSet(nodeJsonFieldCode.toJavaList(String.class)));
        }

        //依赖关系植入
        Boolean dependencyFlag = Boolean.TRUE;
        AbstractDependency targetDependency = AbstractDependency.getAbstractDependencyMap().get(newNode.getNodeType());
        if (Objects.nonNull(targetDependency) && StringUtils.isNotEmpty(newNode.getNodeJson())) {
            dependencyFlag = targetDependency.saveOrUpdateDependency(newNode);
        }

        if (Boolean.FALSE.equals(dependencyFlag) || !isUpdated) {
            throw new ApiException(ErrorCodeEnum.INTERFACE_SAVE_ERROR);
        }

        updateVersionInfo(updateNodeInfoReqVo.getVersionId());

        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(updateNodeInfoReqVo.getEngineCode());
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);

        return newNode;
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveNodeFieldRel(EngineNode engineNode, Set<String> fieldCodes) {

        QueryWrapper<EngineNodeFieldRel> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("engine_version_id", engineNode.getVersionId());
        queryWrapper.eq("engine_node_code", engineNode.getNodeCode());
        List<EngineNodeFieldRel> existsFieldRels = engineNodeFieldService.list(queryWrapper);

        //更新时需要删除旧的依赖关系
        if (existsFieldRels != null && !existsFieldRels.isEmpty()) {
            List<Long> ids = existsFieldRels.stream().map(EngineNodeFieldRel::getId).collect(Collectors.toList());
            engineNodeFieldService.removeByIds(ids);
        }

        List<EngineNodeFieldRel> fieldRels = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(fieldCodes)) {
            fieldCodes.forEach(fieldCode -> {
                EngineNodeFieldRel fieldRel = new EngineNodeFieldRel();
                fieldRel.setEngineNodeCode(engineNode.getNodeCode());
                fieldRel.setEngineNodeName(engineNode.getNodeName());
                fieldRel.setEngineVersionId(engineNode.getVersionId());
                fieldRel.setFieldCode(fieldCode);
                fieldRels.add(fieldRel);
            });
            engineNodeFieldService.saveBatch(fieldRels);
        }
    }

    /**
     * 新增空节点
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public NodeRespVo saveEngineNode(NodeSaveReqVo nodeSaveReqVo) {
        checkVersionStatus(nodeSaveReqVo.getVersionId());
        EngineNode engineNodeNew = nodeSaveReqVo.coverToEngineNode();
        int maxNodeOrder = getMaxNodeOrder(engineNodeNew.getVersionId());
        engineNodeNew.setNodeOrder(maxNodeOrder + 1);
        engineNodeNew.setNodeCode(nodeSaveReqVo.getEngineCode() + "_" + (maxNodeOrder + 1));
        EngineNode engineNode = this.getOne(queryWrapper(engineNodeNew.getNodeCode(), nodeSaveReqVo.getVersionId()));
        if (engineNode != null) {
            throw new ApiException(ENGINE_NODE_CODE_REPEAT.getCode(), ENGINE_NODE_CODE_REPEAT.getMessage());
        }

        this.save(engineNodeNew);
        log.info("[新增空节点]节点信息:{}", engineNodeNew);

        updateVersionInfo(nodeSaveReqVo.getVersionId());

        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(nodeSaveReqVo.getEngineCode());
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);

        NodeRespVo nodeRespVo = new NodeRespVo();
        BeanUtils.copyProperties(engineNodeNew, nodeRespVo);
        return nodeRespVo;
    }

    @Override
    public EngineNode saveEngineNode(EngineNode engineNode) {
        checkVersionStatus(engineNode.getVersionId());
        this.save(engineNode);
        updateVersionInfo(engineNode.getVersionId());
        return engineNode;
    }

    @Override
    public int getMaxNodeOrder(Long versionId) {
        Integer maxNodeOrder = engineNodeMapper.getMaxNodeOrder(versionId);
        return maxNodeOrder == null ? 0 : maxNodeOrder;
    }

    /**
     * 移动节点
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateNodeForMove(NodeMoveReqVo nodeVo) {
        checkVersionStatus(nodeVo.getVersionId());

        EngineNode condition = new EngineNode();
        condition.setVersionId(nodeVo.getVersionId());
        condition.setNodeCode(nodeVo.getNodeCode());

        EngineNode newNode = new EngineNode();
        newNode.setNodeX(nodeVo.getNodeX());
        newNode.setNodeY(nodeVo.getNodeY());

        log.info("[移动节点]节点code:{},版本id:{}", nodeVo.getNodeCode(), nodeVo.getVersionId());

        engineNodeMapper.updateEngineNode(newNode, condition);

        updateVersionInfo(nodeVo.getVersionId());

        updateEngine(nodeVo.getVersionId());

        return true;
    }

    /**
     * 节点重命名
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean renameNode(NodeRenameReqVo nodeRenameReqVo) {
        checkVersionStatus(nodeRenameReqVo.getVersionId());
        String nodeCode = nodeRenameReqVo.getNodeCode();
        String nodeName = nodeRenameReqVo.getNodeName();
        long versionId = nodeRenameReqVo.getVersionId();

        EngineNode engineNode = this.getOne(queryWrapper(nodeCode, versionId));
        log.info("[节点重命名]更新前节点信息:{}", engineNode);
        engineNode.setNodeName(nodeName);
        log.info("[节点重命名]更新后节点信息:{}", engineNode);

        this.updateById(engineNode);

        updateVersionInfo(versionId);
        //更新主表
        updateEngine(versionId);

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeNodeAll(List<Long> engineNodeIds) {
        for (Long nodeId : engineNodeIds) {
            removeNode(nodeId);
        }
    }

    /**
     * 删除节点：
     * 1. 孤立节点，直接删除
     * 2. 有子节点，需更新子节点parentId值为null
     * 3. 有父节点，需更新父节点nextNodeCodes为null
     * 如果父节点是分组/分流/冠军挑战 需要将父节点的nextNodeCode值中删除该节点code
     *
     * @param engineNodeId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeNode(Long engineNodeId) {
        EngineNode engineNode = this.getById(engineNodeId);
        if (engineNode == null) {
            throw exception(ENGINE_NODE_NOT_EXISTS, "节点id:" + engineNodeId);
        }

        log.info("[删除节点]节点信息:{}", engineNode);

        checkVersionStatus(engineNode.getVersionId());

        //删除依赖关系
        EngineNodeComponentRel dependency = new EngineNodeComponentRel();
        dependency.setEngineNodeCode(engineNode.getNodeCode());
        dependency.setEngineVersionId(engineNode.getVersionId());
        engineNodeComponentRelService.removeRel(Collections.singletonList(dependency));

        String parentIds = engineNode.getParentId();
        String nextNodeCodes = engineNode.getNextNodeCodes();
        if (!StringUtil.isBlank(parentIds) || !StringUtil.isBlank(nextNodeCodes)) {
            List<EngineNode> nodeList = getEngineNodeListByEngineVersionId(engineNode.getVersionId());
            List<EngineNode> nodesToUpdate = new ArrayList<>();
            nodesToUpdate.addAll(removeParentIdFromNextNodes(engineNode, nodeList));
            nodesToUpdate.addAll(removeNextNodeCodesFromParentNodes(engineNode, nodeList));
            if (CollectionUtils.isNotEmpty(nodesToUpdate)) {
                updateBatchById(nodesToUpdate, 100);
            }
        }

        //删除陪跑节点信息
        engineActionService.remove(new LambdaQueryWrapper<EngineAction>()
                .eq(EngineAction::getVersionId, engineNode.getVersionId())
                .eq(EngineAction::getNodeCode, engineNode.getNodeCode()));

        //删除节点
        this.removeById(engineNode.getId());

        updateVersionInfo(engineNode.getVersionId());
        updateEngine(engineNode.getVersionId());

    }

    /**
     * 1. 根据当前节点nextNodeCodes获取子节点nodeCode
     * 2. 从nodeList中获取子节点信息
     * 3. 更新子节点parentId(多个->update，单个 ->null)
     *
     * @param node     待删除节点
     * @param nodeList 当前节点所在决策流版本的所有节点列表
     * @return 需要更新parentId的节点
     */
    public List<EngineNode> removeParentIdFromNextNodes(EngineNode node, List<EngineNode> nodeList) {
        Map<String, EngineNode> nodeMap = NodeUtil.convertNodeList2MapNodeCodeKey(nodeList);
        String nextNodes = node.getNextNodeCodes();
        List<EngineNode> resultNodes = new ArrayList<>();
        if (StringUtil.isValidStr(nextNodes)) {
            String[] nextNodeCodes = nextNodes.split(CommonConst.SYMBOL_COMMA);
            for (String nextNodeCode : nextNodeCodes) {
                EngineNode nextNode = nodeMap.get(nextNodeCode);
                if (Objects.nonNull(nextNode)) {
                    NodeUtil.removeParentId(nextNode, String.valueOf(node.getId()));
                    resultNodes.add(nextNode);
                }
            }
            log.info("更新子节点parentId属性,当前节点:{},待更新子节点:{}", node.getNodeCode(), JSON.toJSONString(resultNodes));
        }
        return resultNodes;
    }

    public List<EngineNode> removeNextNodeCodesFromParentNodes(EngineNode node, List<EngineNode> nodeList) {
        Map<Long, EngineNode> nodeMap = NodeUtil.convertNodeList2Map(nodeList);
        String parentId = node.getParentId();
        List<EngineNode> resultNodes = new ArrayList<>();
        if (StringUtil.isValidStr(parentId)) {
            String[] preEngineNodeIdArr = node.getParentId().split(CommonConst.SYMBOL_COMMA);
            for (String preNodeId : preEngineNodeIdArr) {
                EngineNode preNode = nodeMap.get(Long.valueOf(preNodeId));
                if (Objects.nonNull(preNode)) {
                    NodeUtil.removeNextNodeCode(preNode, node.getNodeCode());
                    //处理特殊节点nodeJson
                    preNode = nodeJsonParseService.removeNodeCodeFromNodeJson(preNode, node.getNodeCode());
                    resultNodes.add(preNode);
                }
            }
            log.info("更新父节点nextNodeCodesd属性,当前节点:{},待更新子节点:{}", node.getNodeCode(), JSON.toJSONString(resultNodes));
        }
        return resultNodes;
    }


    @Override
    public boolean removeNodesByIds(List<Long> nodeIds) {
        if (nodeIds == null || nodeIds.isEmpty()) {
            throw exception(PARAM_CHECK_ERROR, "nodeIds为空");
        }
        List<EngineNode> nodeList = this.listByIds(nodeIds);
        if (nodeList == null || nodeList.isEmpty()) {
            throw exception(PARAM_CHECK_ERROR, "节点信息查询异常,nodeIds:{}" + JSONObject.toJSONString(nodeIds));
        }

        List<Long> nodeVersionIds = nodeList.stream().map(EngineNode::getVersionId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(nodeVersionIds) || nodeVersionIds.size() > 1) {
            throw exception(PARAM_CHECK_ERROR, "节点版本信息异常,nodeVersionIds:{}", JSON.toJSONString(nodeVersionIds));
        }

        checkVersionStatus(nodeVersionIds.get(0));

        return this.removeByIds(nodeIds);
    }

    /**
     * 删除连线.
     * 1. 规则集、决策树、决策表、评分卡、子引擎：更新sourceNode.nextNodeCodes、targetNode.parentId
     * 2. 分组、分流、并行、决策选项：更新sourceNode.nodeJson、sourceNode.nextNodeCodes、targetNode.parentId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeLink(NodeLinkReqVo nodeLinkReqVo) {
        Long sourceNodeId = nodeLinkReqVo.getSourceNodeId();
        Long targetNodeId = nodeLinkReqVo.getTargetNodeId();

        EngineNode sourceNode = this.getById(sourceNodeId);
        EngineNode targetNode = this.getById(targetNodeId);

        if (sourceNode == null || targetNode == null) {
            throw exception(ENGINE_NODE_NOT_EXISTS, "节点id:" + nodeLinkReqVo.getSourceNodeId() + "," + nodeLinkReqVo.getTargetNodeId());
        }
        log.info("[删除连线]删除连线前开始节点:{},删除连线前结束节点:{}", sourceNode, targetNode);
        checkVersionStatus(sourceNode.getVersionId());

        //sourceNode与targetNode之间无连线，return
        if (!isLinked(sourceNode, targetNode)) {
            return true;
        }

        NodeUtil.removeNextNodeCode(sourceNode, targetNode.getNodeCode());
        NodeUtil.removeParentId(targetNode, String.valueOf(sourceNode.getId()));

        //3. 处理特殊节点的nodeJson
        sourceNode = nodeJsonParseService.removeNodeCodeFromNodeJson(sourceNode, targetNode.getNodeCode());

        List<EngineNode> eList = new ArrayList<>();
        eList.add(targetNode);
        eList.add(sourceNode);
        updateBatchById(eList);
        log.info("[删除连线]删除连线后开始节点:{},删除连线后结束节点:{}", sourceNode, targetNode);

        //更新主表
        updateEngine(sourceNode.getVersionId());

        return true;
    }

    private boolean isLinked(EngineNode sourceNode, EngineNode targetNode) {
        if (sourceNode == null || targetNode == null) {
            return false;
        }
        String nextNodeCodes = sourceNode.getNextNodeCodes();
        String parentIds = targetNode.getParentId();

        if (StringUtil.isBlank(nextNodeCodes)) {
            return false;
        }
        if (StringUtil.isBlank(parentIds)) {
            return false;
        }
        return nextNodeCodes.contains(targetNode.getNodeCode()) && parentIds.contains(String.valueOf(sourceNode.getId()));
    }

    @Override
    public EngineNode getNodeById(Long nodeId) {
        return this.getById(nodeId);
    }

    /**
     * 节点连线.
     * 1. 规则集、决策树、决策表、评分卡、子引擎：更新sourceNode.nextNodeCodes、targetNode.parentId
     * 2. 分组、分流、并行、决策选项：更新sourceNode.nodeJson、sourceNode.nextNodeCodes、targetNode.parentId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean link(NodeLinkReqVo nodeLinkReqVo) {
        Long sourceNodeId = nodeLinkReqVo.getSourceNodeId();
        Long targetNodeId = nodeLinkReqVo.getTargetNodeId();

        EngineNode sourceNode = getById(sourceNodeId);
        EngineNode targetNode = getById(targetNodeId);

        if (sourceNode == null || targetNode == null) {
            throw new ApiException(ENGINE_NODE_LINK_ERROR.getCode(), ENGINE_NODE_LINK_ERROR.getMessage());
        }

        log.info("[节点连线]连线前开始节点:{},连线前结束节点:{}", sourceNode, targetNode);
        checkVersionStatus(sourceNode.getVersionId());

        if (isLinked(sourceNode, targetNode)) {
            throw exception(ENGINE_NODE_LINK_ERROR_003);
        }

        NodeUtil.addNextNode(sourceNode, targetNode.getNodeCode());
        NodeUtil.addParentId(targetNode, String.valueOf(sourceNode.getId()));

        //2. 分流、分组、冠军挑战连线需要更新源节点json
        String sourceNodeJson = nodeLinkReqVo.getSourceNodeJson();
        if (StringUtil.isValidStr(sourceNodeJson)
                &&
                (NodeTypeEnum.CLASSIFY.getType().equals(sourceNode.getNodeType())
                        || NodeTypeEnum.SANDBOX.getType().equals(sourceNode.getNodeType())
                        || NodeTypeEnum.CHAMPION_CHALLENGE.getType().equals(sourceNode.getNodeType())
                        || NodeTypeEnum.PARALLEL.getType().equals(sourceNode.getNodeType())
                )) {
            sourceNode.setNodeJson(sourceNodeJson);
            JSONObject parseResult = nodeJsonParseService.nodeJsonParser(sourceNode);
            sourceNode.setNodeScript(parseResult.getString("nodeScript"));
            sourceNode.setNextNodeCodes(parseResult.getString("nextNodeCodes"));//分组分流节点的nextNodeCodes以nodeJson中为准

            //校验nextNodes的parentIds是否包含当前sourceNodeId
            if (StringUtils.isNotEmpty(sourceNode.getNextNodeCodes())) {
                //查询除目标节点之外的节点的父级code是否是当前sourceNodeId
                List<EngineNode> engineNodeList = this.list(new LambdaQueryWrapper<EngineNode>()
                        .select(EngineNode::getVersionId,
                                EngineNode::getParentId,
                                EngineNode::getNodeCode,
                                EngineNode::getNodeName)
                        //排除当前目标节点
                        .ne(EngineNode::getNodeCode, targetNode.getNodeCode())
                        .in(EngineNode::getNodeCode, Arrays.asList(sourceNode.getNextNodeCodes().split(SysConstant.COMMA))));
                if (CollectionUtils.isNotEmpty(engineNodeList)) {
                    List<EngineNode> engineNodeListFilter = engineNodeList.stream()
                            .filter(x -> Objects.equals(x.getVersionId(), sourceNode.getVersionId()))
                            .collect(Collectors.toList());
                    //如果全部查不到则说明节点信息和父级节点不在同一个版本
                    if (CollectionUtils.isEmpty(engineNodeListFilter)) {
                        String errorMsg = MessageFormat.format(ENGINE_NODE_LINK_ERROR_002.getMessage(),
                                Joiner.on(SysConstant.COMMA)
                                        .skipNulls()
                                        .join(engineNodeList.stream()
                                                .map(EngineNode::getNodeName).collect(Collectors.toSet())),
                                sourceNode.getNodeName());
                        throw new ApiException(ErrorCodeEnum.ENGINE_NODE_LINK_ERROR_002.getCode(), errorMsg);
                    }
                    //如果在同一个版本,则对比parentId
                    Map<String, String> nodeInfoMap = engineNodeListFilter.stream()
                            .collect(Collectors.toMap(EngineNode::getNodeCode, EngineNode::getNodeName));
                    Map<String, Set<String>> collect = engineNodeListFilter.stream().collect(
                            Collectors.groupingBy(EngineNode::getNodeCode,
                                    Collectors.mapping(EngineNode::getParentId, Collectors.toSet())));
                    List<String> errorNodeName = new ArrayList<>();
                    collect.forEach((key, value) -> {
                        if (value.stream()
                                .map(parentIds -> parentIds.split(SysConstant.COMMA))
                                .flatMap(Arrays::stream)
                                .noneMatch(parentId -> Objects.equals(parentId, sourceNodeId.toString()))) {
                            errorNodeName.add(nodeInfoMap.get(key));
                        }
                    });
                    if (CollectionUtils.isNotEmpty(errorNodeName)) {
                        String errorMsg = MessageFormat.format(ENGINE_NODE_LINK_ERROR_001.getMessage(),
                                Joiner.on(SysConstant.COMMA).skipNulls().join(errorNodeName), sourceNode.getNodeName());
                        throw new ApiException(ErrorCodeEnum.ENGINE_NODE_LINK_ERROR_001.getCode(), errorMsg);
                    }
                }
            }
        }


        List<EngineNode> eList = new ArrayList<>();
        eList.add(targetNode);
        eList.add(sourceNode);

        log.info("[节点连线]连线后开始节点:{},连线后结束节点:{}", sourceNode, targetNode);

        updateBatchById(eList);

        updateVersionInfo(sourceNode.getVersionId());
        //更新主表
        updateEngine(sourceNode.getVersionId());

        return true;
    }

    public void updateEngine(Long versionId) {
        List<EngineVersion> list = engineVersionService.list(
                new LambdaQueryWrapper<EngineVersion>().select(EngineVersion::getEngineCode)
                        .eq(EngineVersion::getId, versionId));
        if (CollectionUtils.isEmpty(list)) {
            throw new ApiException(ENGINE_VERSION_NOT_EXISTS);
        }
        //更新主表的更新人和时间
        EngineUpdateReqVo engine = new EngineUpdateReqVo();
        engine.setEngineCode(list.get(0).getEngineCode());
        engineService.updateEngine(SessionManager.getLoginAccount(), engine);
    }

    /**
     * 复制节点
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String copy(Long nodeId) {
        EngineNode eNode = this.getById(nodeId);
        if (eNode == null) {
            throw new ApiException(ENGINE_NODE_NOT_EXISTS.getCode(), ENGINE_NODE_NOT_EXISTS.getMessage());
        }
        log.info("[复制节点]节点信息:{}", eNode);
        List<DecisionComponentDependencyResp> list = getByEngineVersionId(Collections.singletonList(eNode.getVersionId()));
        if (CollectionUtils.isEmpty(list)) {
            throw new ApiException(ErrorCodeEnum.ENGINE_NODE_NOT_EXISTS);
        }

        checkVersionStatus(eNode.getVersionId());

        // 设置需要更改的属性：坐标偏移、nodeOrder+1、nodeCode、nodeName、nextNodeCodes清空、parentId清空
        int maxOrder = getMaxNodeOrder(eNode.getVersionId());
        eNode.setNodeX(eNode.getNodeX() + 50);
        eNode.setNodeY(eNode.getNodeY() + 50);
        eNode.setNodeOrder(maxOrder + 1);
        eNode.setNodeCode(list.get(0).getEngineCode() + "_" + (maxOrder + 1));
        eNode.setNodeName(eNode.getNodeName() + "_copy");
        eNode.setNextNodeCodes("");
        eNode.setParentId(null);
        //清空分支节点nodeJson中的nextNode，并重新生成nodeScript
        nodeJsonParseService.clearAllNextNodeCodeFromNodeJson(eNode);
        JSONObject parseResult = nodeJsonParseService.nodeJsonParser(eNode);
        eNode.setNodeScript(parseResult.getString("nodeScript"));
        Boolean dependencyFlag = Boolean.TRUE;
        //依赖关系植入
        AbstractDependency targetDependency = AbstractDependency.getAbstractDependencyMap().get(eNode.getNodeType());
        if (Objects.nonNull(targetDependency) && StringUtils.isNotEmpty(eNode.getNodeJson())) {
            dependencyFlag = targetDependency.saveOrUpdateDependency(eNode);
        }
        log.info("[复制节点]生成新节点信息:{}", eNode);
        if (Boolean.FALSE.equals(dependencyFlag) || !this.save(eNode)) {
            throw new ApiException(ErrorCodeEnum.INTERFACE_SAVE_ERROR);
        }


        updateVersionInfo(eNode.getVersionId());
        //更新主表
        updateEngine(eNode.getVersionId());
        return eNode.getNodeCode();
    }

    public EngineNode branchNodeRemoveAllLink(EngineNode node, List<EngineNode> nodeList) {
        List<EngineNode> nodesToUpdate = new ArrayList<>();
        if (NodeUtil.isBranchNode(node.getNodeType())) {
            nodesToUpdate.addAll(removeParentIdFromNextNodes(node, nodeList));
            node = nodeJsonParseService.clearAllNextNodeCodeFromNodeJson(node);
            node.setNextNodeCodes(null);
            nodesToUpdate.add(node);
        }
        if (CollectionUtils.isNotEmpty(nodesToUpdate)) {
            updateBatchById(nodesToUpdate);
        }
        return node;
    }

    @Override
    public List<NodeTypeResponse> getPreviousNodeOutput(NodeVo nodeVo) {
        List<NodeTypeResponse> nodeTypeList = new ArrayList<>();
        EngineNode engineNode = this.getOne(queryWrapper(nodeVo.getNodeCode(), nodeVo.getVersionId()));
        if (engineNode == null) {
            throw exception(ENGINE_NODE_NOT_EXISTS, nodeVo.getEngineCode(), nodeVo.getVersionId());
        }
        Long versionId = engineNode.getVersionId();
        List<EngineNode> nodeList = getEngineNodeListByEngineVersionId(versionId);
        Map<Long, EngineNode> nodeMap = NodeUtil.convertNodeList2Map(nodeList);
        Set<String> nodeTypeSet = nodeList.stream()
                .map(EngineNode::getNodeType)
                .filter(item ->
                        !item.equals(NodeTypeEnum.START.getType())
                                && !item.equals(NodeTypeEnum.END.getType())
                                && !item.equals(CLASSIFY.getType())
                                && !item.equals(NodeTypeEnum.SANDBOX.getType()))
                .collect(Collectors.toSet());
        for (String nodeType : nodeTypeSet) {
            NodeTypeEnum nodeTypeEnum = NodeTypeEnum.adapad(nodeType);
            Map<Long, EngineNode> previousNodeMap = new HashMap<>();
            recursivePreviousNode(engineNode, nodeMap, nodeTypeEnum, previousNodeMap);
            List<EngineNode> previousNodes = new ArrayList<>(previousNodeMap.values());
            List<NodeInfoResponse> nodeInfoList = new ArrayList<>();

            switch (nodeTypeEnum) {
                case RULE_BASE:
                    nodeInfoList = getRuleNodeInfo(previousNodes, nodeTypeEnum);
                    break;
                case SCORECARD:
                case DECISION_TABLES:
                case DECISION_TREE:
                case DECISION_FLOW:
                case DECISION:
                    nodeInfoList = getSingleStrategyNodeInfo(previousNodes, nodeTypeEnum);
                    break;
                default:
                    break;
            }

            NodeTypeResponse nodeTypeResponse = new NodeTypeResponse();
            nodeTypeResponse.setNodeType(nodeTypeEnum.getType());
            nodeTypeResponse.setNodeTypeName(nodeTypeEnum.getType());
            nodeTypeResponse.setNodeInfoList(nodeInfoList);
            nodeTypeList.add(nodeTypeResponse);
        }
        return nodeTypeList;
    }

    @Override
    public List<DecisionComponentDependencyResp> getByEngineVersionId(List<Long> versionIds) {
        return engineNodeMapper.getByEngineCodes(versionIds);
    }

    @Override
    public List<String> getFieldCodesByEngineVersionId(Long engineVersionId) {
        List<EngineNode> list = this.list(new LambdaQueryWrapper<EngineNode>()
                .select(EngineNode::getFieldCodes)
                .eq(EngineNode::getVersionId, engineVersionId)
                .isNotNull(EngineNode::getFieldCodes)
                .ne(EngineNode::getFieldCodes, StringUtils.EMPTY));
        return CollectionUtils.isEmpty(list) ? Lists.newArrayList() :
                list.stream().map(EngineNode::getFieldCodes).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
    }

    @Override
    public List<ActionListVo> findActionListByServerType(Integer actionServerType) {
        List<ActionListVo> list = new ArrayList<>();
        if (Objects.equals(actionServerType, ActionServerEnum.OLD_ACTION_SERVER.getCode())) {
            for (OldModelActionEnum value : OldModelActionEnum.values()) {
                ActionListVo actionListVo = new ActionListVo();
                actionListVo.setType(value.getType());
                actionListVo.setDesc(value.getDesc());
                list.add(actionListVo);
            }
        }
        return list;
    }

    /**
     * 向上递归节点
     *
     * @param currentNode
     */
    private void recursivePreviousNode(EngineNode currentNode, Map<Long, EngineNode> nodeMap, NodeTypeEnum nodeTypeEnum, Map<Long, EngineNode> previousNodeMap) {
        if (StringUtils.isNotBlank(currentNode.getParentId())) {
            List<String> parentIdList = Arrays.asList(currentNode.getParentId().split(CommonConst.SYMBOL_COMMA));
            for (String parentId : parentIdList) {
                EngineNode exNode = nodeMap.get(Long.valueOf(parentId));
                if (exNode != null && exNode.getNodeType().equals(nodeTypeEnum.getType())) {
                    previousNodeMap.put(exNode.getId(), exNode);
                }
                if (exNode != null) {
                    recursivePreviousNode(exNode, nodeMap, nodeTypeEnum, previousNodeMap);
                }
            }
        }
    }

    /**
     * 获取单个策略的节点信息集合
     *
     * @param previousNodes
     * @param nodeTypeEnum
     * @return
     */
    private List<NodeInfoResponse> getSingleStrategyNodeInfo(List<EngineNode> previousNodes, NodeTypeEnum nodeTypeEnum) {
        List<NodeInfoResponse> nodeInfoList = new ArrayList<>();
        for (EngineNode engineNode : previousNodes) {
            if (StringUtils.isBlank(engineNode.getNodeJson())) {
                continue;
            }
            String resultFieldEn = "";
            String resultFieldCn = "";
            Integer valueType = 1;
            JSONObject nodeJson = JSON.parseObject(engineNode.getNodeJson());
            JSONObject jsonObject = null;
            switch (nodeTypeEnum) {
                case SCORECARD:
//                    Scorecard scorecard = scorecardService.getById();
                    jsonObject = JSON.parseObject(JSON.toJSONString(nodeJson.getJSONArray("scorecardList").get(0)));
                    ScorecardVersionVo versionVo = scorecardVersionService.queryById(jsonObject.getLong("versionId"));
                    if (StringUtils.isNotBlank(versionVo.getResultFieldCode())) {
                        resultFieldEn = versionVo.getResultFieldCode();
                    } else {
                        resultFieldEn = engineNode.getNodeType() + "_" + engineNode.getId() + "_" + versionVo.getId() + "_score";
                    }
                    resultFieldCn = "评分";
                    valueType = 1;
                    break;
                case DECISION_TABLES:
                    jsonObject = JSON.parseObject(JSON.toJSONString(nodeJson.getJSONArray("decisionTableList").get(0)));
                    DecisionTablesVo decisionTablesVo = decisionTablesService.queryDetailsById(jsonObject.getLong("decisionTableId"));
                    List<DecisionTablesVersionVo> versionList = decisionTablesVo.getVersionList();
                    for (DecisionTablesVersionVo decisionTablesVersionVo : versionList) {
                        if (decisionTablesVersionVo.getId().equals(jsonObject.getLong("versionId"))) {
                            resultFieldEn = decisionTablesVersionVo.getResultFieldCode();
                            if (StringUtils.isBlank(resultFieldEn)) {
                                resultFieldEn = engineNode.getNodeType() + "_" + engineNode.getId() + "_" + decisionTablesVersionVo.getId() + "_result";
                            }
                            break;
                        }
                    }
//                    DecisionTables decisionTables = decisionTablesService.getById(Long.valueOf(engineNode.getNodeJson()));
//                    resultFieldEn = decisionTables.getResultFieldEn();
                    resultFieldCn = "决策结果";
                    valueType = 2;
                    break;
                case DECISION_TREE:
                    //决策树
                    jsonObject = JSON.parseObject(JSON.toJSONString(nodeJson.getJSONArray("decisionTreeList").get(0)));
                    DecisionTreeVo decisionTree = decisionTreeService.queryById(jsonObject.getLong("decisionTreeId"));
                    List<DecisionTreeVersionVo> decisionTreeVersionList = decisionTree.getVersionList();
                    for (DecisionTreeVersionVo decisionTreeVersionVo : decisionTreeVersionList) {
                        if (decisionTreeVersionVo.getId().equals(jsonObject.getLong("versionId"))) {
                            resultFieldEn = decisionTreeVersionVo.getResultFieldCode();
                            if (StringUtils.isBlank(resultFieldEn)) {
                                resultFieldEn = engineNode.getNodeType() + "_" + engineNode.getId() + "_" + decisionTreeVersionVo.getId() + "_result";
                            }
                            break;
                        }
                    }
                    resultFieldCn = "决策结果";
                    valueType = 2;
                    break;
                case DECISION_FLOW:
                    resultFieldEn = engineNode.getNodeType() + "_" + engineNode.getId() + "_" + engineNode.getNodeJson() + "_result";
                    resultFieldCn = "引擎结果";
                    valueType = 2;
                    break;
                case DECISION:
                    resultFieldEn = engineNode.getNodeType() + "_" + engineNode.getId() + "_result";
                    resultFieldCn = "决策选项结果";
                    valueType = 2;
                    break;
                default:
                    break;
            }

            NodeStrategyOutputResponse nodeStrategyOutputResponse = new NodeStrategyOutputResponse();
            nodeStrategyOutputResponse.setFieldEn(resultFieldEn);
            nodeStrategyOutputResponse.setFieldCn(resultFieldCn);
            nodeStrategyOutputResponse.setValueType(valueType);
            List<NodeStrategyOutputResponse> strategyOutputList = new ArrayList<>();
            strategyOutputList.add(nodeStrategyOutputResponse);

            NodeInfoResponse nodeInfoResponse = new NodeInfoResponse();
            nodeInfoResponse.setNodeId(engineNode.getId());
            nodeInfoResponse.setNodeName(engineNode.getNodeName());
            nodeInfoResponse.setStrategyOutputList(strategyOutputList);
            nodeInfoList.add(nodeInfoResponse);
        }

        return nodeInfoList;
    }

    private List<NodeInfoResponse> getRuleNodeInfo(List<EngineNode> previousNodes, NodeTypeEnum nodeTypeEnum) {
        List<NodeInfoResponse> nodeInfoList = new ArrayList<>();
        for (EngineNode engineNode : previousNodes) {
            List<RuleInfoOutputResponse> ruleInfoList = new ArrayList<>();
            List<Long> ruleIds = new ArrayList<>();
            List<Long> complexRuleVersionIds = new ArrayList<>();
            List<Long> scriptRuleVersionIds = new ArrayList<>();
            JSONObject nodeJson = JSONObject.parseObject(engineNode.getNodeJson());
            if (nodeJson == null) {
                return nodeInfoList;
            }

            JSONArray jsonArray = null;
            // 1:串行 2:并行
            int groupType = nodeJson.getInteger("groupType") == null ? Constants.ruleNode.EXECUTEGROUP : nodeJson.getInteger("groupType");
            if (groupType == Constants.ruleNode.MUTEXGROUP) {
                jsonArray = nodeJson.getJSONObject("mutexGroup").getJSONArray("rules");
            } else {
                jsonArray = nodeJson.getJSONObject("executeGroup").getJSONArray("rules");
            }
            Integer difficulty = null;
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject ruleObj = jsonArray.getJSONObject(i);
                difficulty = ruleObj.getInteger("difficulty");
                Long ruleVersionId = ruleObj.getLong("ruleVersionId");
                ruleIds.add(ruleObj.getLong("id"));
                if (difficulty == null) {
                    continue;
                }
                switch (difficulty) {
                    case 1:
//                        ruleIds.add(ruleObj.getLong("userId"));
                        break;
                    case 2:
                        complexRuleVersionIds.add(ruleVersionId);
                        break;
                    case 3:
                        scriptRuleVersionIds.add(ruleVersionId);
                        break;
                }
            }
            List<RuleSetNodeResultParam> ruleSetNodeResultParams = new ArrayList<>();
            List<RuleInfo> ruleList = new ArrayList<>();
            Map<Long, RuleInfo> ruleMap = new HashMap<>();
            // 简单规则
            if (CollectionUtils.isNotEmpty(ruleIds)) {
                ruleList.addAll(ruleService.listByIds(ruleIds));
            }

            // 复杂规则
            if (CollectionUtils.isNotEmpty(complexRuleVersionIds)) {
                List<RuleVersion> ruleVersionList = ruleVersionService.listByIds(complexRuleVersionIds);
                if (CollectionUtils.isNotEmpty(ruleVersionList)) {
                    for (RuleVersion ruleVersion : ruleVersionList) {
//                        String resultEn = ruleVersion.getResultFieldEn();
//                        String scoreEn = ruleVersion.getScoreFieldEn();
//                        RuleInfo rule = ruleMap.get(ruleVersion.getRuleId());
//                        ruleSetNodeResultParams.add(new RuleSetNodeResultParam(ruleVersion.getRuleId(),2,ruleVersion.getId(),resultEn,scoreEn,rule.getCode(),rule.getName()));
                    }
                }
            }

            //脚本规则
            if (CollectionUtils.isNotEmpty(scriptRuleVersionIds)) {
                List<RuleScriptVersion> ruleScriptVersionList = ruleScriptVersionService.listByIds(scriptRuleVersionIds);
                if (CollectionUtils.isNotEmpty(ruleScriptVersionList)) {
                    for (RuleScriptVersion ruleVersion : ruleScriptVersionList) {
                        String resultEn = "ruleHitResult";
                        String scoreEn = "ruleHitScore";
                        RuleInfo rule = ruleMap.get(ruleVersion.getRuleId());
                        ruleSetNodeResultParams.add(new RuleSetNodeResultParam(ruleVersion.getRuleId(), 3, ruleVersion.getId(), resultEn, scoreEn, rule.getCode(), rule.getName()));
                    }
                }
            }

            for (RuleSetNodeResultParam rule : ruleSetNodeResultParams) {
                RuleInfoOutputResponse ruleInfoOutputResponse = new RuleInfoOutputResponse();
                ruleInfoOutputResponse.setId(rule.getId());
                ruleInfoOutputResponse.setCode(rule.getCode());
                ruleInfoOutputResponse.setName(rule.getName());

                List<NodeStrategyOutputResponse> ruleOutputList = new ArrayList<>();
                NodeStrategyOutputResponse hitOutputResponse = new NodeStrategyOutputResponse();
                String resultEn = rule.getResultEn();
                if (StringUtils.isBlank(resultEn)) {
                    if (null != rule.getVersionId()) {
                        resultEn = "rule_" + rule.getDifficulty() + "_" + rule.getId() + "_" + rule.getVersionId() + "_hitResult";
                    } else {
                        resultEn = "rule_" + rule.getDifficulty() + "_" + rule.getId() + "_hitResult";
                    }

                }
                hitOutputResponse.setFieldEn(resultEn);
                hitOutputResponse.setFieldCn("是否命中");
                hitOutputResponse.setValueType(2);
                ruleOutputList.add(hitOutputResponse);

                NodeStrategyOutputResponse scoreOutputResponse = new NodeStrategyOutputResponse();
                String scoreEn = rule.getScoreEn();
                if (StringUtils.isBlank(scoreEn)) {
                    if (null != rule.getVersionId()) {
                        scoreEn = "rule_" + rule.getDifficulty() + "_" + rule.getId() + "_" + rule.getVersionId() + "_score";
                    } else {
                        scoreEn = "rule_" + rule.getDifficulty() + "_" + rule.getId() + "_score";
                    }
                }
                scoreOutputResponse.setFieldEn(scoreEn);
                scoreOutputResponse.setFieldCn("得分");
                scoreOutputResponse.setValueType(1);
                ruleOutputList.add(scoreOutputResponse);
                ruleInfoOutputResponse.setRuleOutputList(ruleOutputList);

                ruleInfoList.add(ruleInfoOutputResponse);
            }

            List<NodeStrategyOutputResponse> statisticsOutputList = new ArrayList<>();
            NodeStrategyOutputResponse statisticsSizeOutput = new NodeStrategyOutputResponse();
            statisticsSizeOutput.setFieldEn(engineNode.getNodeType() + "_" + engineNode.getId() + "_size");
            statisticsSizeOutput.setFieldCn("规则命中个数");
            statisticsSizeOutput.setValueType(1);
            statisticsOutputList.add(statisticsSizeOutput);

            NodeStrategyOutputResponse statisticsScoreOutput = new NodeStrategyOutputResponse();
            statisticsScoreOutput.setFieldEn(engineNode.getNodeType() + "_" + engineNode.getId() + "_score");
            statisticsScoreOutput.setFieldCn("规则总得分");
            statisticsScoreOutput.setValueType(1);
            statisticsOutputList.add(statisticsScoreOutput);

            RuleOutputResponse ruleOutput = new RuleOutputResponse();
            ruleOutput.setStatisticsOutputList(statisticsOutputList);
            ruleOutput.setRuleInfoList(ruleInfoList);

            NodeInfoResponse nodeInfoResponse = new NodeInfoResponse();
            nodeInfoResponse.setNodeId(engineNode.getId());
            nodeInfoResponse.setNodeName(engineNode.getNodeName());
            nodeInfoResponse.setRuleOutput(ruleOutput);
            nodeInfoList.add(nodeInfoResponse);
        }

        return nodeInfoList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(EngineNode entity) {
        checkVersionStatus(entity.getVersionId());
        super.updateById(entity);

        updateVersionInfo(entity.getVersionId());

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateByNodeCodeAndVersionId(String nodeCode, Long versionId, EngineNode engineNode) {
        checkVersionStatus(versionId);
        EngineNode condition = new EngineNode();
        condition.setNodeCode(nodeCode);
        condition.setVersionId(versionId);
        engineNodeMapper.updateEngineNode(engineNode, condition);

        updateVersionInfo(versionId);

        return true;
    }

    /**
     * 更新决策流版本表创建时间、更新时间
     */
    private void updateVersionInfo(Long engineVersionId) {
        EngineVersion engineVersion = new EngineVersion();
        engineVersion.setId(engineVersionId);
        engineVersion.setUpdatedTime(DateUtil.now());
        engineVersion.setUpdateUserId(SessionManager.getLoginUserId());
        engineVersionService.updateById(engineVersion);
    }

    private void checkVersionStatus(Long versionId) {
        EngineVersion engineVersion = engineVersionService.getById(versionId);
        if (engineVersion == null) {
            throw exception(ENGINE_VERSION_NOT_EXISTS);
        }
        Integer versionStatus = engineVersion.getStatus();
        if (versionStatus.equals(EngineVersionStatusEnum.ENABLE.getStatus()) ||
                versionStatus.equals(EngineVersionStatusEnum.DISABLE.getStatus()))
            throw exception(CANNOT_BE_MODIFIED_VERSION_STATUS, EngineVersionStatusEnum.getName(versionStatus));
    }
}
